#pragma once

#include <compl.h>

/* __vprint(putchar, fmt, ...) */
/* 需要外部输入 */
/* 可以构造成一个eff union把eff都打包 */

typedef void (*const __vputchar_t)(const char c);
/* 相关运算 */

/* 泛型运算 */
#define XMOD(x, denom) TIF_ELSE(denom == 16, x & 15, TIF_ELSE(denom == 10, x - x / 10 * 10, (void)0))

#define XDIV(x, denom) TIF_ELSE(denom == 16, x >> 4, TIF_ELSE(denom == 10, x / 10, (void)0))

#define VPRINT_ADD_NEG(x, outchar)    \
	{                             \
		if (x < 0) {          \
			outchar('-'); \
			n++;          \
			x = -x;       \
		}                     \
	}

SAI int __vputs(const char *s, __vputchar_t outc)
{
	for (; *s; s++) {
		outc(*s);
	}
	outc('\n');
	return 0;
}

SAI word_t xdiv(word_t x, uint_t denom)
{
	switch (denom) {
	case 16:
		return x >> 4;
	case 10:
		return x / 10;
	default:
		return 0;
	}
}

// /* 除零举错 */
SAI word_t xmod(word_t x, uint_t denom)
{
	switch (denom) {
	case 16:
		return x & 15;
	case 10:
		return x - x / 10 * 10;
	default:
		return 0;
	}
}

SAI PURE bool_t _isdigit(char c)
{
	return c >= '0' && c <= '9';
}

SAI PURE int _atoi(char c)
{
	return c - '0';
}

SAI uint_t print_spaces(int n, __vputchar_t outc)
{
	for (int i = 0; i < n; i++) {
		outc(' ');
	}
	return n;
}

SAI uint_t print_string(const char *s, __vputchar_t outc)
{
	uint_t n;
	for (n = 0; *s; s++, n++) {
		outc(*s);
	}
	return n;
}

SAI uint_t print_unsigned_long(word_t x, word_t ui_base, __vputchar_t outc)
{
	char out[sizeof(word_t) * 2 + 3];
	uint_t i, j;
	uint_t d;

	/*
     * Only base 10 and 16 supported for now. We want to avoid invoking the
     * compiler's support libraries through doing arbitrary divisions.
     */
	if (ui_base != 10 && ui_base != 16) {
		return 0;
	}

	if (x == 0) {
		outc('0');
		return 1;
	}

	for (i = 0; x; x = xdiv(x, ui_base), i++) {
		d = xmod(x, ui_base);

		if (d >= 10) {
			out[i] = 'a' + d - 10;
		} else {
			out[i] = '0' + d;
		}
	}

	for (j = i; j > 0; j--) {
		outc(out[j - 1]);
	}

	return i;
}

/* The print_unsigned_long_long function assumes that an unsinged int
   is half the size of an word_t long */

SAI uint_t print_unsigned_long_long(ullong_t x, uint_t ui_base, __vputchar_t outc)
{
	uint_t upper, lower;
	uint_t n = 0;
	uint_t mask = 0xF0000000u;
	uint_t shifts = 0;

	/* only implemented for hex, decimal is harder without 64 bit division */
	if (ui_base != 16) {
		return 0;
	}

	/* we can't do 64 bit division so break it up into two hex numbers */
	upper = (uint_t)(x >> 32llu);
	lower = (uint_t)x & 0xffffffff;

	/* print first 32 bits if they exist */
	if (upper > 0) {
		n += print_unsigned_long(upper, ui_base, outc);
		/* print leading 0s */
		while (!(mask & lower)) {
			outc('0');
			n++;
			mask = mask >> 4;
			shifts++;
			if (shifts == 8) {
				break;
			}
		}
	}
	/* print last 32 bits */
	n += print_unsigned_long(lower, ui_base, outc);

	return n;
}

SAI int __vprintf(const char *format, __va_list_t ap, __vputchar_t outc)
{
	uint_t n;
	uint_t formatting;
	int nspaces = 0;

	if (!format) {
		return 0;
	}

	n = 0;
	formatting = 0;
	while (*format) {
		if (formatting) {
			while (_isdigit(*format)) {
				nspaces = nspaces * 10 + _atoi(*format);
				format++;
				if (format == NULL_PTR) {
					break;
				}
			}
			switch (*format) {
			case '%':
				outc('%');
				n++;
				format++;
				break;

			case 'd': {
				int x = __va_arg(ap, int);

				VPRINT_ADD_NEG(x, outc);
				// if (x < 0) {
				//     outc('-');
				//     n++;
				//     x = -x;
				// }

				n += print_unsigned_long(x, 10, outc);
				format++;
				break;
			}

			case 'u':
				n += print_unsigned_long(__va_arg(ap, uint_t), 10, outc);
				format++;
				break;

			case 'x':
				n += print_unsigned_long(__va_arg(ap, uint_t), 16, outc);
				format++;
				break;

			case 'p': {
				word_t p = __va_arg(ap, word_t);
				if (p == 0) {
					n += print_string("(nil)", outc);
				} else {
					n += print_string("0x", outc);
					n += print_unsigned_long(p, 16, outc);
				}
				format++;
				break;
			}

			case 's':
				n += print_string(__va_arg(ap, char *), outc);
				format++;
				break;

			case 'l':
				format++;
				switch (*format) {
				case 'd': {
					long x = __va_arg(ap, long);

					VPRINT_ADD_NEG(x, outc);
					// if (x < 0) {
					//     outc('-');
					//     n++;
					//     x = -x;
					// }

					n += print_unsigned_long((word_t)x, 10, outc);
					format++;
				} break;
				case 'l':
					if (*(format + 1) == 'x') {
						n += print_unsigned_long_long(__va_arg(ap, ullong_t), 16, outc);
					} else if (*(format + 1) == 'u') {
						n += print_unsigned_long_long(__va_arg(ap, ullong_t), 10, outc);
					} else if (*(format + 1) == 'd') {
						llong_t x = __va_arg(ap, llong_t);
						VPRINT_ADD_NEG(x, outc);
						n += print_unsigned_long_long(x, 10, outc);
					}
					format += 2;
					break;
				case 'u':
					n += print_unsigned_long(__va_arg(ap, word_t), 10, outc);
					format++;
					break;
				case 'x':
					n += print_unsigned_long(__va_arg(ap, word_t), 16, outc);
					format++;
					break;

				default:
					/* format not supported */
					return -1;
				}
				break;
			default:
				/* format not supported */
				return -1;
			}

			n += print_spaces(nspaces - n, outc);
			nspaces = 0;
			formatting = 0;
		} else {
			switch (*format) {
			case '%':
				formatting = 1;
				format++;
				break;

			default:
				outc(*format);
				n++;
				format++;
				break;
			}
		}
	}

	return n;
}

/* 填空函数，拿来填效应 */

#define DEF_PRINTF(pfunc_name, output_handler)            \
	SAI int pfunc_name(const char *fmt, ...)          \
	{                                                 \
		__va_list_t args;                         \
		word_t i;                                 \
		__va_start(args, fmt);                    \
		i = __vprintf(fmt, args, output_handler); \
		__va_end(args);                           \
		return i;                                 \
	}
